package de.blitzcoder.collide.gui;

import de.blitzcoder.collide.Interface;
import de.blitzcoder.collide.engine.FileOpener;
import de.blitzcoder.collide.engine.SearchEngine;
import de.blitzcoder.collide.gui.textarea.CollIDETextArea;
import java.io.File;
import javax.swing.Icon;
import de.blitzcoder.collide.util.FileUtilities;
import de.blitzcoder.collide.util.OS;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import org.gjt.sp.jedit.buffer.BufferListener;
import org.gjt.sp.jedit.buffer.JEditBuffer;

/**
 *
 * @author blitzcoder
 */
public class CodeDocument extends Document {

    // Gadgets
    protected CollIDETextArea textArea;
    private SearchBar searchBar;

    // Properties
    protected File file;
    protected boolean saved;
    private String mode;

    // Helpers
    private SearchEngine searchEngine;

    protected CodeDocument() {}

    public CodeDocument(File file,String mode) {

        // Properties
        this.file = file;
        saved = true;
        this.mode = mode;


    }

    private String getFileExtension() {
        String name = file.getName();
        int lastIndex = name.lastIndexOf('.');
        if (lastIndex != -1) {
            return name.substring(lastIndex+1);
        }
        return "";
    }

    public boolean equalsFile(File file) {
        return this.file.equals(file);
    }

    public void load() throws Exception {

        // Create and setup the gui
        textArea = new CollIDETextArea();
        searchBar = new SearchBar(textArea);
        searchBar.setVisible(false);
        setLayout(new GridBagLayout());

        if (mode != null)
            textArea.setFileExtension(getFileExtension(), mode);

        GridBagConstraints c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 0;
        c.weightx = 1.0;
        c.weighty = 1.0;
        c.fill = c.BOTH;
        add(textArea, c);

        c.weighty = 0.0;
        c.gridy = 1;
        add(searchBar, c);

        // Create Helpers
        searchEngine = new SearchEngine(textArea);

        // Some TextArea setup
        textArea.setMinimumSize(new Dimension(1, 1));
        try {
            loadCode();
        } catch (FileNotFoundException ex) {
            JOptionPane.showMessageDialog(Interface.getDialogParent(), "Die Datei " + file.getAbsolutePath() + " konnte nicht geöffnet werden da sie nicht gefunden wurde.");
            throw new Exception(ex);
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(Interface.getDialogParent(), "Die Datei " + file.getAbsolutePath() + " konnte nicht geöffnet werden (" + ex.getClass().getName() + ")");
            throw new Exception(ex);
        } catch (OutOfMemoryError err) {
            JOptionPane.showMessageDialog(Interface.getDialogParent(), "Die Datei " + file.getAbsolutePath() + " konnte nicht geöffnet werden da sie zu groß ist (" + file.length() + " Bytes)");
            throw new Exception(err);
        }
        textArea.setCaretPosition(0);

        textArea.getBuffer().addBufferListener(new BufferListener() {

            public void foldLevelChanged(JEditBuffer arg0, int arg1, int arg2) {
            }

            public void contentInserted(JEditBuffer buf, int startLine, int offset, int numLines, int length) {
                saved = false;
                fireDocumentUpdate();
            }

            public void contentRemoved(JEditBuffer arg0, int arg1, int arg2, int arg3, int arg4) {
                saved = false;
                fireDocumentUpdate();
            }

            public void preContentInserted(JEditBuffer arg0, int arg1, int arg2, int arg3, int arg4) {
            }

            public void preContentRemoved(JEditBuffer arg0, int arg1, int arg2, int arg3, int arg4) {
            }

            public void transactionComplete(JEditBuffer arg0) {
            }

            public void foldHandlerChanged(JEditBuffer arg0) {
            }

            public void bufferLoaded(JEditBuffer arg0) {
            }
        });

        textArea.addKeyListener(new TemplateKeyListener(textArea));

        if (file != null && !file.canWrite()) {
            textArea.getBuffer().setReadOnly(true);
        }

    }

    public void documentInserted(JPopupMenu menu) {
        
    }

    public static BlitzMaxCodeDocument getCompileCode() {
        int index = Interface.get().toolBar.actionSelectBuildFile.getSelectedIndex();
        if (index == 0) {
            Document doc = Interface.getDocumentManager().getCurrentDocument();
            if (doc instanceof BlitzMaxCodeDocument) {
                return (BlitzMaxCodeDocument) doc;
            }
        } else {
            Document[] docs = Interface.getDocumentManager().getAllDocuments();
            int index_ = 1;
            for (Document doc:docs) {
                if (doc instanceof BlitzMaxCodeDocument) {
                    if (index == index_) {
                        return (BlitzMaxCodeDocument) doc;
                    }
                    index_++;
                }
            }
        }
        return null;
    }

    public static CodeDocument getDocumentByFile(File file) {
        return (CodeDocument)Interface.getDocumentManager().getDocumentForFile(file);
    }

    public void selectLine(int line) {
        textArea.setCaretPosition(textArea.getLineStartOffset(line));
        textArea.selectLine();
    }

    public CollIDETextArea getTextArea() {
        return textArea;
    }

    public SearchEngine getSearchEngine() {
        return searchEngine;
    }

    protected void loadCode() throws IOException, FileNotFoundException, OutOfMemoryError {

        FileInputStream stream = new FileInputStream(file);

        if (stream != null) {

            byte[] data = new byte[stream.available()];
            stream.read(data);

            String code = new String(data);

            textArea.setText(code);
            textArea.clearUndo();

            return;
        }

    }


    public void saveAs() {
        JFileChooser chooser = new JFileChooser(file.getParentFile());
        int ret = chooser.showSaveDialog(Interface.get());
        if (ret == JFileChooser.APPROVE_OPTION) {

            this.file = chooser.getSelectedFile();
            saveCode();
            close();
            FileOpener.open(file);

        }
    }

    public void saveCode() {

        // delete the backup file, if exists
        File backupFile = new File(file.getAbsolutePath() + "~");
        if (backupFile.exists()) {
            try {
                backupFile.delete();
            } catch (Exception e) {
                JOptionPane.showMessageDialog(Interface.get(), "Konnte Backup Datei nicht löschen. " + backupFile.getAbsolutePath());
                return;
            }
        }

        // Rename the Realfile to backup-file
        try {
            file.renameTo(backupFile);

            if (OS.isWindows()) {
                FileUtilities.hideFile(backupFile);
            }

        } catch (Exception e) {
            JOptionPane.showMessageDialog(Interface.get(), "Konnte " + this.file + " nicht zu " + backupFile + " umbenennen.");
            return;
        }

        // write new File
        try {
            PrintWriter newFile = new PrintWriter(new BufferedWriter(new FileWriter(this.file)));
            newFile.write(textArea.getText());
            newFile.close();
        } catch (Exception e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(Interface.get(), "Konnte nicht in " + this.file + " schreiben.");
            // Try to copy old file
            try {
                backupFile.renameTo(file);
            } catch (Exception ex) {
                ex.printStackTrace();
                JOptionPane.showMessageDialog(Interface.get(), "Konnte " + backupFile + " nicht nach " + this.file + " kopieren.");
                return;
            }
            return;
        }

        saved = true;
        fireDocumentUpdate();
    }

    public File getFile() {
        return file;
    }

    public void activateSearchBar() {
        searchBar.setVisible(true);
        searchBar.activate();
    }

    @Override
    public String getTitle() {
        return (saved ? file.getName() : file.getName() + "*") + (textArea.isEditable() ? "" : "[r/o]");
    }

    @Override
    public boolean close() {
        if (!saved) {
            int ans = JOptionPane.showConfirmDialog(Interface.get(), this.file + " ist nicht gespeichert. Soll nun gespeichert werden?");
            switch (ans) {
                case JOptionPane.CANCEL_OPTION:
                    return false;
                case JOptionPane.YES_OPTION:
                    saveCode();
                    return true;
                case JOptionPane.NO_OPTION:
                    return true;
            }
            return false;
        } else {
            return true;
        }
    }

    @Override
    public Icon getIcon() {
        if (getFileExtension().toLowerCase().equals("bmx"))
            return de.blitzcoder.collide.icons.Icon.load("file_bmax.png");
        return de.blitzcoder.collide.icons.Icon.load("file_text.png");
    }

    @Override
    public String getToolTip() {
        return file.getAbsolutePath();
    }

    @Override
    public void focus() {
        
    }

    @Override
    public boolean isClonable() {
        return true;
    }

    @Override
    public Document cloneDocument() {
        return new CloneCodeDocument(this);
    }

}
